open Ast
open Gen
open Arbreintermediaire


let rec construireComparateur comparateur = 
  match comparateur with
  Inf -> IInf
  | Sup -> ISup
  | InfEgal -> IInfEgal
  | SupEgal -> ISupEgal
  | Ast.Egal -> ICompEgal
  | Diff -> IDiff
;;


let rec construireOperateur operateur =  
  match operateur with
  Plus -> IPlus
  | Moins -> IMoins
  | Mult -> IMult
  | Div -> IDiv
;;

let rec construireExpressionArithmetique a = 
  match a with
  NN (entier) -> IConstante(entier)
  | NUnop (a) -> IUnop(construireExpressionArithmetique(a))
  | NBinOp (a1,op,a2) -> IBinop(construireExpressionArithmetique(a1) , construireOperateur(op) ,construireExpressionArithmetique(a2) )  
  | NVariable (nom,entry) ->
      IRegister(Registre(entry.entry))
;;

let rec constuireExpressionBoolean boolean =
  match boolean with
  NTrue ->  ITrue
  | NFalse -> IFalse
  | NCmp (a1,comparateur,a2) -> 
      ICmp(construireExpressionArithmetique(a1), construireComparateur(comparateur), construireExpressionArithmetique(a2)) 
  | NotBoolean (boolean) ->  
      INotBoolean(constuireExpressionBoolean(boolean)) 
  | NOr (booleen1,booleen2) ->  
      IOr(constuireExpressionBoolean(booleen1),constuireExpressionBoolean(booleen2))
  | NAnd (booleen1,booleen2) ->
      IAnd (constuireExpressionBoolean(booleen1),constuireExpressionBoolean(booleen2)) 
;;


let listearguments = ref [];;

let rec constuire_arguments noeud =
  match noeud with 
  ArL(liste) ->
  begin
    listearguments := [];
    List.iter (fun (v) -> 
      match v with
      | NVariable (nom,entry) ->
      listearguments := !listearguments@[IRegister(Registre (entry.entry))];
      | NN n ->
         listearguments := !listearguments@[IConstante n]
      | NUnop a ->
        listearguments := !listearguments@[
          IUnop(construireExpressionArithmetique(a))
        ]
      | NBinOp (a1,op,a2) ->
        listearguments := !listearguments@[
          IBinop(construireExpressionArithmetique(a1),construireOperateur(op),construireExpressionArithmetique(a2))
        ]
    ) liste;
    !listearguments
  end
;;

let rec constuireAppel(appel) =
  match appel with
  NAppel(nom_fonction,arguments) ->
  IFunCall(nom_fonction,constuire_arguments(arguments));
;;

let rec construireBlockVide() =
  let lbl_mort = Gen.named_label("MORT") in
    IBlock(lbl_mort,INop)

let rec construireInstruction(instruction,maFrame) = 
  match instruction with
  | NReturn ->
    IStatements(
    [
      IGoto(maFrame.lreturn)
      ;
      IBlockStatement(construireBlockVide())
    ])
  | NReturnValeur(a) ->
    IStatements(
    [
      IEgal(
        IRegExpr(
          maFrame.result, 
          construireExpressionArithmetique(a)
        )
      )
      ;
      IGoto(maFrame.lreturn)
      ;
      IBlockStatement(construireBlockVide())
    ])

  | NAffectation (NVariable(nom,entry),a) ->
    IEgal( 
      IRegExpr(
        Registre(entry.entry), 
        construireExpressionArithmetique(a)
      )
    )  
  
  | NAppelMethode (appel) ->
    IFunc(constuireAppel(appel))
  
  | NAppelFonction (NVariable(nom,entry),appel) ->
    IStatements(
    [
      IEgal(
        IRegFunc(
          Registre(entry.entry),
          constuireAppel(appel)
        )
      )
    ])

  | _ -> INop
;;

let rec construireStatement(noeud,maFrame) =

  let rec construireStatements (liste,laFrame) =
  match liste with
  | [] -> []
  | e::r -> [construireStatement(e,maFrame)]@(construireStatements(r,laFrame))
in 

match noeud with
| NIf(nBoolean,nStatementThen,nStatementElse) -> 
let lThen = Gen.named_label("THEN") in
let lElse = Gen.named_label("ELSE") in
let lEndif = Gen.named_label("ENDIF") in
let leJumpIf = {
testIf= constuireExpressionBoolean(nBoolean);
labelThen= lThen;
labelElse= lElse;
labelEndif= lEndif;
blockThen= IBlock(lThen, 
  IStatements([
    construireStatement (nStatementThen,maFrame)
    ;
    IGoto(lEndif)
  ])
);
blockElse= IBlock(lElse, 
  IStatements([
    construireStatement (nStatementElse,maFrame)
    ;
    IGoto(lEndif)
  ])
);
} in
IJumpIf leJumpIf

| NWhile(nBoolean,nStatementLoop) ->
let lTest = Gen.named_label("TEST") in
let lLoop = Gen.named_label("LOOP") in
let lEnd = Gen.named_label("END") in
let leJumpWhile = {
labelTest= lTest;
testWhile= constuireExpressionBoolean(nBoolean);
labelLoop= lLoop;
blockLoop= IBlock(lLoop,
  IStatements([ 
    construireStatement (nStatementLoop,maFrame)
    ;
    IGoto(lTest)
  ])
);
labelEnd= lEnd;
} in
IJumpWhile leJumpWhile

| NBlock(_,statementsListe) ->
IStatements (construireStatements(statementsListe,maFrame)) 
| NStatementInstruction (instruction) ->
IStatements [construireInstruction(instruction,maFrame)]
;;

let rec construireArgument noeud = 
  match noeud with 
  | NArgument (NVariable(nom,entry),_) ->
  Registre (entry.entry)
  | _ -> Registre (-2)
;;

let listeargumentsdeclaration = ref [];;

let rec construireArgumentsdeclarationListe noeud = 
  match noeud with
  Al(nArguments) -> 
  begin
    listeargumentsdeclaration := [];
    List.iter (fun (arg) -> 
      let reg = construireArgument(arg) in
      listeargumentsdeclaration := !listeargumentsdeclaration@[reg]
    ) nArguments ;
    !listeargumentsdeclaration  (*renvoie la liste des arguments *)
  end
;;


let rec construireProcedure noeud =
  match noeud with
  NProcedure(nom,args,statement) -> 
  let laFrame =  {
  name = nom;                      
  lentry =  Gen.named_label(nom);
  lreturn = Gen.named_label("FIN_"^nom);
  args = construireArgumentsdeclarationListe(args);
  result = Registre 0;
  stack = 0;
  } in
  BlockFrame(laFrame, 
    IBlock(
      laFrame.lentry,
      IStatements([
        construireStatement(statement,laFrame)
        ;
        IBlockStatement(IBlock(laFrame.lreturn,INop))
      ])
   ))
;;

let rec construireFonction noeud =
  match noeud with
  NFonction(nom,args,letype,statement) -> 
  let laFrame =  {
  name = nom;                      
  lentry =  Gen.named_label(nom);
  lreturn = Gen.named_label("FIN_"^nom);
  args = construireArgumentsdeclarationListe(args);
  result = Registre 0;
  stack = 0;
  } in
  BlockFrame(laFrame, 
    IBlock(
      laFrame.lentry,
      IStatements([
        construireStatement(statement,laFrame)
        ;
        IBlockStatement(IBlock(laFrame.lreturn,INop))
      ])
   ))
;;

(*Permet de construire des blockframe*)
let rec construireBlockFrame noeud =
  match noeud with 
  | NProgrammeFonction(nFonction) -> construireFonction(nFonction)
  | NProgrammeProcedure(nProcedure) ->construireProcedure(nProcedure)
;;

(*
Permet de construire l'arbre intermediaire 
Je pars du noeud principal
*)

let listeFonctions = ref [];;

let rec construire noeud = 
  match noeud with
  | Programme(fonctions,main) -> 
  begin
      let frameMain =  {
        name = "main";                      
        lentry = Gen.named_label("MAIN");
        lreturn = Gen.named_label("EXIT");
        args = [];
        result = Registre 0;
        stack = 0;
      } in
        begin
          listeFonctions := [];
          List.iter (fun x -> 
            listeFonctions := !listeFonctions@[construireBlockFrame(x)]
          ) fonctions;  
            let blockmain = IBlock (frameMain.lentry, 
              IStatements([
                construireStatement(main,frameMain)
                ;
                IBlockStatement(IBlock(frameMain.lreturn,INop))
              ])) in

            {text= blockmain; frames= !listeFonctions}
        end
  end
;;


